// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: Implementation of CElementWithValue.
//   $Id$


#include "stdafx.h"
#include "elementwithvalue.h" 
#include "XFormsXFormsElement.h"
#include "SingleNodeBinding.h"
#include "PEValueElement.h"

#ifdef __NEWUI
#endif

CElementWithValue::CElementWithValue(void)
	:m_pValueElement(0)
	,m_pValueObject(0)
{
}

CElementWithValue::~CElementWithValue(void)
{
}


STDMETHODIMP CElementWithValue::Detach()
{

	HRESULT hr = __super::Detach();
	if(m_pValueElement)
	{
		m_pValueElement.Release();
	}
	return hr;
}

HRESULT CElementWithValue::valueElementReady(CPEValueElement * pValueElement)
{
	HRESULT hr;
	if(m_pValueObject)
	{
		hr = E_FAIL;
	}
	else
	{
		m_pValueObject = pValueElement;
		hr = S_OK;
	}
	return hr;
}

HRESULT CElementWithValue::changeUI()
{
	HRESULT hr = S_FALSE;
	CImplSingleNodeBinding * pNB = dynamic_cast<CImplSingleNodeBinding *>(m_pFormControl);
	if(pNB)
	{
		CComBSTR sType;
		pNB->get_type(&sType);
		hr = resetSchemaTypeForUI(sType);
		if(S_OK == hr && m_pValueObject) 
		{
			hr = m_pValueObject->changeUI();
		}

	}

	return hr;
}

/// <summary>
/// <c>CreateValuePseudoElement()</c> creates the pe--value element which will become the 
/// the container for the UIScript.
/// <param name="pMasterEl">HTML element which is the parent of the pseudo element thats to be created</param>
/// </summary>
HRESULT CElementWithValue::CreateValuePseudoElement(IHTMLElement * pMasterEl)
{
	ASSERT(pMasterEl);
	HRESULT hr = E_FAIL;

	//Seek the label child of the master, insert pe--value after this, 
	//	otherwise, insert it at the beginning.
	CComPtr<IDispatch> pDisp;
	pMasterEl->get_children(&pDisp);
	CComQIPtr<IHTMLElementCollection> pColl(pDisp);
	bool bDoInsert = true;
	CComQIPtr<IHTMLElement> pElPreviousSibling;
	if(pColl)
	{
		long len;
		pColl->get_length(&len);
		CComVariant vtIterator(0);
		CComVariant vt0(0l);
		for( ; vtIterator.lVal < len; ++vtIterator.lVal)
		{
			CComPtr<IDispatch> pDispEl;
			pColl->item(vtIterator,vt0,&pDispEl);
			CComQIPtr<IHTMLElement> pEl = pDispEl;
			
			if(pEl)
			{
				CComBSTR s;
				pEl->get_tagName(&s);
				
				if(s == L"pe--value")
				{
					bDoInsert = false;
					break;
				}

				if(!pElPreviousSibling && s == L"label")
				{
					// There is a label element in the list of children. 
					// Insert the pe-value element after it.
					pElPreviousSibling = pEl;
					bDoInsert = true;
				}
			}
		}
	}

	if(bDoInsert)
	{
		if(pElPreviousSibling)
		{
			hr = InsertValuePseudoElement(pElPreviousSibling, L"afterEnd");
		}
		else
		{
			hr = InsertValuePseudoElement(pMasterEl, L"afterBegin");
		}
	}

	if(!m_pValueElement)
	{
		// Need to get a pointer to the pe-value element
		CComPtr<IDispatch> pDisp;
		pMasterEl->get_children(&pDisp);
		CComQIPtr<IHTMLElementCollection> pColl(pDisp);
		CComPtr<IDispatch> pDispEl; 
				
		if(pColl)		
			pColl->item(CComVariant(L"valuePseudoElement"),CComVariant(0l),&pDispEl);

		if(pDispEl)
			pDispEl->QueryInterface(IID_IHTMLElement, reinterpret_cast<void**>(&m_pValueElement));

		attachValueEvents();

	}
	return hr;
}



HRESULT CElementWithValue::InsertValuePseudoElement(IHTMLElement * pEl, BSTR bsWhere)
{
	
	HRESULT hr = E_FAIL;
	
	CComBSTR bsPE;
	m_pXForms->GetValueElementString(&bsPE);
	
	hr = pEl->insertAdjacentHTML(bsWhere, bsPE);


	return hr;
}

HRESULT CElementWithValue::GetInitialValueClassName(BSTR * psClassName)
{
	HRESULT hr = S_FALSE;
	if(!!m_sInitialClassName)
	{
		m_sInitialClassName.CopyTo(psClassName);
		hr = S_OK;
	}
	return hr;
}

HRESULT CElementWithValue::render()
{
	HRESULT hr = S_OK;

	CComPtr<IHTMLElement> pMaster;
	hr = GetMasterElement(&pMaster);

	if(SUCCEEDED(hr))
        hr = CreateValuePseudoElement(pMaster);
	
	if(false)
	{
		CComPtr<IXMLDOMNode> pNode;
		getXML(&pNode);
		CComBSTR s;
		pNode->get_xml(&s);
		::MessageBoxW(0,s,0,0);
		s.Empty();
		pMaster->get_outerHTML(&s);
		::MessageBoxW(0,s,0,0);
	}
	return hr;
}

void CElementWithValue::populateEvents(void)
{
}

void  CElementWithValue::populateValueEvents()
{
	m_lstValueEvents.push_back(&EVENT_FP_VALUECHANGED);
	m_lstValueEvents.push_back(&EVENT_FP_VALUECHANGING);
	m_lstValueEvents.push_back(&EVENT_FP_FOCUSIN);
	m_lstValueEvents.push_back(&EVENT_FP_FOCUSOUT);
	m_lstValueEvents.push_back(&EVENT_FP_ACTIVATE);
	m_lstValueEvents.push_back(&EVENT_FP_HINT);
	m_lstValueEvents.push_back(&EVENT_FP_NEXT);
	m_lstValueEvents.push_back(&EVENT_FP_PREVIOUS);
	m_lstValueEvents.push_back(&EVENT_FP_UIREADY);
}

HRESULT CElementWithValue::put_value(BSTR bsValue)
{
	HRESULT hr = E_FAIL;
	if(m_pValueElement)
	{
		if(m_sCurrentValue==bsValue)
		{
			hr = S_FALSE;
		}
		else
		{
			hr = onfp_putvalue(bsValue);
			if(SUCCEEDED(hr))
			{
				m_sCurrentValue.Empty();
				m_sCurrentValue = bsValue;
			}
		}
	}
	return hr;
}

HRESULT CElementWithValue::detachValueEvents()
{
	HRESULT hr = S_OK;
	
	CComQIPtr<IHTMLElement2> pValue2 = m_pValueElement;
	CComBSTRPtrList::iterator i = m_lstValueEvents.begin();

	while(i != m_lstValueEvents.end()  && SUCCEEDED(hr))
	{
		hr = pValue2->detachEvent((*i)->m_str ,m_pEventSink);
		++i;
	}

	return hr;
}

HRESULT CElementWithValue::detachEvents()
{
	HRESULT hr = __super::detachEvents();
	
	hr = detachValueEvents();

	// Bug 1109: Detach events.
	CComPtr<IHTMLElement> pMaster;
	GetMasterElement(&pMaster);
	CComQIPtr<IHTMLElement2> pEl2 = pMaster;
	if(pEl2 && m_pEventSink) {
		pEl2->detachEvent(CComBSTR(L"onkeypress"), m_pEventSink);
		pEl2->detachEvent(CComBSTR(L"onkeyup"), m_pEventSink);
	}

	return hr;
}

HRESULT CElementWithValue::focus()
{
	return onfp_putfocus();
}


HRESULT CElementWithValue::ElementReady()
{
	
	HRESULT hr;
	hr = __super::ElementReady();

	return hr;
}

HRESULT CElementWithValue::DocumentReady()
{
	HRESULT hr = __super::DocumentReady();

		CComBSTR sType, sP3PType;
	CImplSingleNodeBinding * pSNB = dynamic_cast<CImplSingleNodeBinding *>(m_pFormControl);

	// Get the schema type for the bound node
	if(pSNB)
	{
		pSNB->get_type(&sType);
		pSNB->get_p3pType(&sP3PType);
	}

	bool bOnfp_uirefresh = false;

	CComPtr<IHTMLElement> pMaster;
	GetMasterElement(&pMaster);

	if(sType)
	{
		BSTR bsAttrName = ::SysAllocString(L"schemaType");
		VARIANT vtAttrValue;
		vtAttrValue.vt = VT_BSTR;
		vtAttrValue.bstrVal = sType.Detach();

		// Create the schemaType attribute and added the type
		hr = pMaster->setAttribute(bsAttrName, vtAttrValue, 1);
		::SysFreeString(bsAttrName);
		::SysFreeString(vtAttrValue.bstrVal);
		
		// Need to force the UIScript to refresh to pickup the schema
		// type change so that the correct control can be displayed.
		bOnfp_uirefresh = true;
	}

	if(sP3PType)
	{
		BSTR bsAttrName = ::SysAllocString(L"p3ptype");
		VARIANT vtAttrValue;
		vtAttrValue.vt = VT_BSTR;
		vtAttrValue.bstrVal = sP3PType.Detach();

		// Create the schemaType attribute and added the type
		hr = pMaster->setAttribute(bsAttrName, vtAttrValue, 1);
		::SysFreeString(bsAttrName);
		::SysFreeString(vtAttrValue.bstrVal);
		
		// Need to force the UIScript to refresh to pickup the schema
		// type change so that the correct control can be displayed.
		bOnfp_uirefresh = true;
	}

	if(bOnfp_uirefresh)
		onfp_uirefresh();

	// Bug 1109: Fire DOMActivate on Enter.
	CComQIPtr<IHTMLElement2> pEl2 = pMaster;
	if(pEl2) {
		VARIANT_BOOL bResult = VARIANT_FALSE;
		if(!m_pEventSink) {
			CComObject<CEventSink> *pSink;
			CComObject<CEventSink>::CreateInstance(&pSink);
			if(pSink) {
				pSink->m_pControl = this;
				pSink->m_pFormControl = m_pFormControl;
				pSink->m_bHandleThroughHandleEvent = true;
				m_pEventSink = pSink;
				if(m_pEventSink) {
					pEl2->attachEvent(CComBSTR(L"onkeypress"), m_pEventSink, &bResult);
					pEl2->attachEvent(CComBSTR(L"onkeyup"), m_pEventSink, &bResult);
				}
			}
		}
	}
	
	return hr;
}


HRESULT CElementWithValue::attachValueEvents()
{
	HRESULT hr = S_OK;
	populateValueEvents();
/*	
	if(!m_pEventSink)
	{
		CComObject<CEventSink> * pSink;
		hr = CComObject<CEventSink>::CreateInstance(&pSink);
		m_pEventSink = pSink;
	}
*/	
	if (SUCCEEDED(hr))
	{
		CComQIPtr<IEventTarget> pValueAsTarget(m_pValueElement);
		CComQIPtr<IEventListener> pThisAsListener;
		QueryInterface(IID_IEventListener,(void**)&pThisAsListener);
		if(pValueAsTarget)
		{
			CComBSTRPtrList::iterator i = m_lstValueEvents.begin();

			while(i != m_lstValueEvents.end()  && SUCCEEDED(hr))
			{
				hr = pValueAsTarget->addEventListener((*i)->m_str,pThisAsListener,false);
				++i;
			}
		}
	}

	return hr;
}

STDMETHODIMP CElementWithValue::handleEvent(IDOMEvent * pEvt)
{
	HRESULT hr = S_FALSE;
	if(pEvt)
	{
		CComBSTR sEventName;
		pEvt->get_type(&sEventName);
		// Remove will this is not a formcontrol
		if(m_pFormControl) {
			if(sEventName == L"fp-valuechanged")
			{
				CComQIPtr<IMutationEvent> pMutation(pEvt);
				if(pMutation)
				{
					CComBSTR sVal = 0;
					pMutation->get_newValue(&sVal);
					// If enabled then update the instance
					// else blank the control as is not relavent
					m_sCurrentValue = sVal;
					if(m_bEnabled)
						hr = m_pFormControl->putValue(sVal.m_str);
					//else
					//	hr = putDisabledValue();
					/* -- removed while developing celementwithvalue object */
				}
			}
			else if(sEventName == "fp-valuechanging")
			{
				BOOL bIncremental = false;
				
				m_pFormControl->getIncremental(&bIncremental);

				if(bIncremental) 
				{
					CComQIPtr<IMutationEvent> pMutation(pEvt);
					if(pMutation)
					{
						CComBSTR sVal;
						pMutation->get_newValue(&sVal);
						m_sCurrentValue = sVal;
						hr = m_pFormControl->putValue(sVal.m_str);
					}
				}
			}
			else if(sEventName == L"fp-hint")
			{
				hr = m_pFormControl->onhelp();
			}
			else if(sEventName == L"fp-focusout")
			{
				hr = m_pFormControl->onDOMFocusOut();
			}
			else if(sEventName == L"fp-focusin")
			{
				if(m_pFormControl)
				{
					// The DOMFocusIn Event is now fired on the HTML focus event. The move 
					// is due to a problems with the cursor not moving with the focus when 
					// handled on the HTML focusin event.
					hr = m_pFormControl->onDOMFocusIn();
				}
			}
			else if(sEventName == L"fp-activate")
			{
				hr = m_pFormControl->onactivate();
			}
			else if(sEventName == L"fp-next")
			{
				hr = m_pFormControl->onnext();
			}
			else if(sEventName == L"fp-previous")
			{
				hr = m_pFormControl->onprevious();
			}
			else if(sEventName == L"fp-uiready")
			{
				CImplSingleNodeBinding * p = dynamic_cast<CImplSingleNodeBinding *> (m_pFormControl);
				if(p)
				{
					BSTR sData = 0;
					p->getValue(&sData);
					hr = onfp_putvalue(sData);
					::SysFreeString(sData);
				}
			}
			else
			{
				//Should not normally reach this point.
				::AtlTrace("'formsplayer.dll': unhandled event '%S' occured on control %X",sEventName,this);
			}
		}
	}
	return hr;
}

HRESULT CElementWithValue::handleEvent(CComBSTR &sEventName, IHTMLEventObj2 *pEventObject, VARIANT *pvResult)
{
	HRESULT hr = S_FALSE;

	// Bugs 1117 and 1109: Fire DOMActivate and confirm changes to the bound data on >ENTER<
	if(sEventName == L"keypress") {
		CComQIPtr<IHTMLEventObj> pEv = pEventObject;
		if(pEv && m_pValueElement) {
			long nCode = 0;
			pEv->get_keyCode(&nCode);
			if(nCode == 13 && m_bIsButton == false) {
				// Despatch DOMActivate to the value child and allow the event to bubble, rather than
				// despatch it straight to the form control element. By doing this, we allow the XBL
				// for the control to provide its own handler for the event, before it bubbles to any
				// xf:action handlers that may be present in the form.
				//
				// So, instead of this:
				//     m_pFormControl->onactivate();
				//
				// We do this:
				CComPtr<IDOMEvent> pDOMActivate;
				pDOMActivate.CoCreateInstance(CLSID_DOMEvent);
				if(pDOMActivate) {
					pDOMActivate->initEvent(CComBSTR(L"DOMActivate"), VARIANT_TRUE, VARIANT_TRUE);
					CComQIPtr<IEventTarget> pTarget = m_pValueElement;
					if(pTarget) {
						BOOL nResult;
						hr = pTarget->dispatchEvent(pDOMActivate, &nResult);
					}
				}
			}
		}
	}

	if(hr != S_OK)
		hr = __super::handleEvent(sEventName, pEventObject, pvResult);

	return hr;
}

